#include "propertymapper.h"
#include <QMetaProperty>
#include <QDebug>
#include <QEvent>
#include <QDynamicPropertyChangeEvent>

PropertyMapper::PropertyMapper(QObject *mapToObject, const char *mapToMethod, QObject *parent):
    QObject(parent), m_mapTo(mapToObject), m_toMethod(mapToMethod)
{}

int PropertyMapper::addProperty(const QString &propertyName,
                                 const char *mappingPropertyName,
                                 QObject *mappingObject,
                                 bool isQuickProperty)
{
    if (m_propertyIndices.contains(propertyName)) {
        qWarning() << "can't create" << propertyName << "property, alraedy exist!";
        return -1;
    }

    int propertyIdx =
            mappingObject->metaObject()->indexOfProperty(mappingPropertyName);
    QMetaProperty metaProperty = mappingObject->metaObject()->property(propertyIdx);

    int id = m_properties.size();
    m_propertyIndices[propertyName] = id;
    m_properties.push_back({propertyName, metaProperty.type(),
                            mappingPropertyName, mappingObject,
                            isQuickProperty, QVariant()});

    int signalId = metaProperty.notifySignalIndex();
    if (signalId < 0) {
        qWarning() << "can't create" << propertyName << "notify signal didn't exist";
        return -1;
    }

    if (!QMetaObject::connect(mappingObject, signalId,
                         this, id + metaObject()->methodCount())) {
        qWarning() << "can't connect to notify signal:" << mappingPropertyName;
        return -1;
    }

    // add dynamic property
    //setProperty(propertyName.toLocal8Bit().constData(), QVariant(0));
    return id;
}

void PropertyMapper::setMappedProperty(const QString &name, const QVariant &value)
{
    int id = m_propertyIndices.value(name, -1);
    if (id == -1) {
        qWarning() << "setMappedProperty: property" << name << "didn't exist";
        return;
    }
    const property_t &p = m_properties[id];
    p.mappingObject->setProperty(p.mappingName, value);
}

QVariant PropertyMapper::mappedProperty(const QString &name) const
{
    int id = m_propertyIndices.value(name, -1);
    if (id == -1) {
        qWarning() << "mappedProperty: property" << name << "didn't exist";
        return QVariant();
    }
    const property_t &p = m_properties[id];
    return p.mappingObject->property(p.mappingName);
}

int PropertyMapper::qt_metacall(QMetaObject::Call call, int id, void **arguments)
{
    id = QObject::qt_metacall(call, id, arguments);
    if (id < 0 || call != QMetaObject::InvokeMetaMethod)
        return id;
    Q_ASSERT(id < m_properties.size());

    property_t &p = m_properties[id];

    QVariant value;
    if (p.isQuickProperty) {
        value = p.mappingObject->property(p.mappingName);
    } else {
        const void *data = arguments[1];
        value = QVariant(p.type, data);
    }

    if (value != p.lastValue) {
        p.lastValue = value;
        QMetaObject::invokeMethod(m_mapTo, m_toMethod,
                                  Q_ARG(QString, p.name),
                                  Q_ARG(QVariant, value));
    }

    return -1;
}

bool PropertyMapper::setProperty(const char *name, const QVariant &value)
{
    qDebug() << "HEYYY setPoperty" << name << value;
    QString strName(name);
    if (!m_propertyIndices.contains(strName))
        return false;
    property_t &p = m_properties[m_propertyIndices[strName]];
    p.lastValue = value;
    p.mappingObject->setProperty(p.mappingName, value);
    return true;
}

bool PropertyMapper::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::DynamicPropertyChange) {
        QDynamicPropertyChangeEvent *changeEvent = (QDynamicPropertyChangeEvent *)event;
        qDebug() << "p change!" << changeEvent->propertyName();
    }

    return QObject::eventFilter(object, event);
}
